// 工具函数

// 数组去重
function norepeat(arr){
    return arr.reduce(function (prev,item){
        prev.indexOf(item) === -1 && prev.push(item);
        return prev;
    },[]);
}

// 获取查询字符串
function getQueryString(key){
    // 获取当前页面url地址
    var url = location.search;
    // 匹配某个key的值
    var reg = new RegExp('[?&]'+key+'=([^&#]*)');
    // 执行匹配
    if (reg.test(url)) {
        return RegExp.$1;
    } else {
        return '';
    }
}

// 生成指定区间的随机整数
function random(min,max){
    return Math.round(Math.random()*(max-min))+min;
}

// 随机生成十六进制颜色值
function randomColor(){
    var color = '#';
    var num = '0123456789abcdef';
    // 随机取出6个十六进制数
    for (var i = 0; i < 6; i++){
        var index = random(0,15);
        color += num[index];
    }
    // 返回随机颜色值
    return color;
}

// 随机生成x位验证码
function randomCode(x){
    x = x || 6;// 设置形参的默认值
    var code = '';
    // 循环生成x个随机字符（字母或数字）
    for (var i = 0; i < x; i++) {
        do {
            // 生成随机的ASCII码值
            var ascii = random(48,122);// 60
        } while((ascii >= 58 && ascii <= 64) || (ascii >= 91 && ascii <= 96));
        // 将ASCII码值转成对应的字符并拼接
        code += String.fromCharCode(ascii);
    }
    return code;
}

// 获取dom元素
function $(selector){
    var dom = document.querySelectorAll(selector);
    // 判断dom的长度
    if (dom.length === 1) {
        return dom[0];
    }
    else if (dom.length > 1){
        return dom;
    }
    else {
        return null;
    }
}

// 获取元素在页面中的坐标
function offset(dom){
    var offset_left = 0 - dom.clientLeft;
    var offset_top = 0 - dom.clientTop;
    // dom = box3 -> box2 -> box1 -> body -> null
    while(dom){
        offset_left += dom.clientLeft + dom.offsetLeft;
        offset_top += dom.clientTop + dom.offsetTop;
        dom = dom.offsetParent;
    }
    return {left: offset_left, top: offset_top};
}

/**
 * animate函数
 * params {dom} 要运动的元素
 * params {attr} 要运动的属性
 * params {target} 运动的目标位置
 * params {callback} 运动完成后的回调函数，可选
 * 功能说明：
 * 1.支持任意带px单位的属性运动，如 left top width marginLeft fontSize ...
 * 2.支持opacity的属性运动，范围0-100
 * 3.支持scrollLeft和scrollTop的属性运动
 * 4.支持一个元素依次执行多个运动
 * 5.支持一个元素同时执行多个属性的运动
  */
function animate(options){
    // options = {
    //     el: box,
    //     cb: function (){
    //         box.style.backgroundColor = 'green';
    //     },
    //     attrs: {
    //         left: 1100,
    //         top: 500
    //     }
    // }
    // 要运动的属性的三个关键点：
    // 属性名
    // 属性初始值
    // 属性目标值

    // 将 options.attrs 格式化为如下格式：
    // {
    //     left: {
    //         current: 10,
    //         target: 1100
    //     },
    //     top: {
    //         current: 50,
    //         target: 500
    //     }
    // }

    // 遍历options.attrs对象
    for (var attr in options.attrs){
        // attr -> 'left'  'top'

        // 获取el元素初始的值和目标值
        if (attr === 'opacity') {
            var current = getComputedStyle(options.el)[attr] * 100;// 1 * 100
        }
        else if (attr === 'scrollLeft' || attr === 'scrollTop'){
            var current = options.el[attr];
        }
        else{
            var current = parseInt(getComputedStyle(options.el)[attr]);// '588px' -> 588
        }
        var target = options.attrs[attr];

        // 修改options.attrs的数据结构
        options.attrs[attr] = {
            'current': current,
            'target': target
        }
    }
    // console.log( options.attrs );

    // 先清除再启动计时器
    clearInterval(options.el.timer);
    options.el.timer = setInterval(function (){
        for (var attr in options.attrs){
            // attr -> 'left'  'top'

            // 计算生成一个不断变化的速度
            var speed = (options.attrs[attr].target-options.attrs[attr].current)/10;// 58.8  52.7  45.5  37.3 ...
            // 浏览器识别最小是1px，所以需要对speed取整
            // 速度不能为0，到达不了目标位置
            // speed为正数时，向上取整，保证速度最小为1
            // speed为负数时，向下取整，保证速度最小为1
            speed = speed > 0 ? Math.ceil(speed) : Math.floor(speed);

            // 当前位置累加速度
            options.attrs[attr].current += speed;

            // 判断运动停止条件：剩余运动量 <= 每次的运动量
            if (Math.abs(options.attrs[attr].target-options.attrs[attr].current) <= Math.abs(speed)) {
                // 到达目标位置
                if (attr === 'opacity') {
                    options.el.style[attr] = options.attrs[attr].target/100;
                }
                else if (attr === 'scrollLeft' || attr === 'scrollTop'){
                    options.el[attr] = options.attrs[attr].target;
                }
                else{
                    options.el.style[attr] = options.attrs[attr].target + 'px';
                }
                // 删除options.attrs对象中运动完成的属性
                delete options.attrs[attr];

                for (var key in options.attrs){
                    // 对象有属性才会执行这里
                    return;// 结束执行当前函数
                }

                // 清除计时器
                clearInterval(options.el.timer);
                console.log( '到达目标位置。。。' );

                // 运动完成后执行回调函数
                typeof options.cb === 'function' && options.cb();
            }
            else{
                // 修改box的位置
                if (attr === 'opacity') {
                    options.el.style[attr] = options.attrs[attr].current/100;
                }
                else if (attr === 'scrollLeft' || attr === 'scrollTop'){
                    options.el[attr] = options.attrs[attr].current;
                }
                else{
                    options.el.style[attr] = options.attrs[attr].current + 'px';
                }
            }
        }
        
    },20);
}

// 防抖函数
function debounce(callback,delay){
    var timer;
    delay = delay || 300;// 默认值
    return function (){// newFun
        clearTimeout(timer);
        timer = setTimeout(function (){
            callback();// 执行回调函数
        },delay);
    }
}

// 节流函数
function throttle(callback,delay){
    var lastTime = 0;// 记录上次触发事件的时间
    var timer;// 记录计时器id
    return function (){// newFun
        var nowTime = Date.now();// 记录时间触发时间
        // 判断上次触发事件和当前触发事件的时间差
        if ((nowTime-lastTime) > delay) {
            // 当前和上次触发事件的时间间隔大于delay毫秒
            callback();
            // 更新上次触发事件的时间
            lastTime = nowTime;
        }
        else {
            // 保证最后再执行一次
            clearTimeout(timer);
            timer = setTimeout(function (){
                // 在delay时间之后再执行一次
                callback();
                // 更新上次触发事件的时间
                lastTime = nowTime;
            },delay);
        }
    }
}

// 浅拷贝：如果对象的属性值为引用类型时，只拷贝引用地址
function lightCopy(obj){
    var newObj = {};
    for (var key in obj){
        newObj[key] = obj[key];
    }
    return newObj;
}

// 深拷贝：引用类型不是只拷贝引用地址
// 通常对应函数和正则只需要浅拷贝即可
function deepCopy(obj){
    var result;// 保存拷贝出来的值

    // 判断obj的数据类型
    if (typeof obj === 'object') {
        // obj是：null [] {} 正则
        
        // 判断obj是否为null
        if (obj === null) {
            result = null;
        }
        // 判断obj是否为数组
        else if (obj.constructor === Array){
            result = [];
            for (var i = 0; i < obj.length; i++) {
                // result.push(obj[i]);
                result.push(deepCopy(obj[i]));
            }
        }
        // 判断obj是否为正则对象
        else if (obj.constructor === RegExp){
            result = obj;
        }
        else {
            // obj是{}对象
            result = {};
            for (var key in obj){
                result[key] = deepCopy(obj[key]);
                // result.name = deepCopy('海静')
            }
        }
    } else {
        // obj是：基本类型 函数
        result = obj;
    }

    // 返回拷贝的出来的值
    return result;
}

// 设置cookie
function setCookie(option){
    // 解构赋值并设置默认值
    let {key,val,domain='',path='',exp=''} = option;
    let d;
    // 判断是否需要设置有效期
    if (exp === '') {
        d = '';
    } else {
        d = new Date();
        d.setMinutes(d.getMinutes()+exp);
    }
    document.cookie = `${key}=${val}; domain=${domain}; path=${path}; expires=${d}`;
}

// 获取cookie
function getCookie(key){
    let arr1 = document.cookie.split('; ');
    for (var i = 0; i < arr1.length; i++) {
        let arr2 = arr1[i].split('=');
        if (arr2[0] === key) {
            return arr2[1];
        }
    }
    return null;
}

// 删除cookie
function rmCookie(key){
    setCookie({
        key: key,
        val: '1234',
        exp: -60*24 // 设置为过期的cookie
    });
}

// ajax封装
function ajax(option){
    // 解构参数
    let {
        timeout=0,
        type='get',
        cached=true,
        dataType='text',
        url,
        data='',
        // success,
        // error
    } = option;

    return new Promise((resolve,reject)=>{
        // 创建xhr对象
        let xhr = new XMLHttpRequest();

        // 超时设置（毫秒）
        // 指定时间没有响应会自动终止请求 xhr.abort()
        xhr.timeout = timeout;

        // 判断data数据类型（查询字符串/对象）
        if (Object.prototype.toString.call(data) === '[object Object]') {
            // 将对象格式化成查询字符串
            let str = '';
            for (let key in data){
                str += key+'='+data[key]+'&'
            }
            data = str.substr(0,str.length-1);// 去掉最后的&
        }

        // 判断请求方式
        if (type.toLowerCase() === 'get') {
            // 初始化请求
            let time = '';
            !cached && (time = '&_='+Date.now());
            xhr.open(type,url+'?'+data+time,true);

            // 将请求发送到服务端
            xhr.send(null);
        }
        else if (type.toLowerCase() === 'post'){
            // 初始化请求
            xhr.open(type,url,true);

            // 设置请求头
            xhr.setRequestHeader('Content-Type','application/x-www-form-urlencoded');

            // 将请求发送到服务端
            xhr.send(data);
        }
        else {
            throw new Error('目前只支持get和post请求');
        }

        // 等待请求/响应状态改变
        xhr.onreadystatechange = function (){
            // 判断请求状态
            if (xhr.readyState === 4) {// 请求完成
                // 判断响应状态
                if (xhr.status >= 200 && xhr.status < 300) {// 响应成功
                    // 请求成功之后要做的事（可以自定义）
                    if (dataType === 'xml') {
                        // success(xhr.responseXML);
                        resolve(xhr.responseXML);
                    }
                    else if (dataType === 'json'){
                        // 将JSON字符串转成JSON对象
                        let json = JSON.parse(xhr.responseText);
                        // success(json);
                        resolve(json);
                    }
                    else {
                        // success(xhr.responseText);
                        resolve(xhr.responseText);
                    }
                } else {// 响应失败
                    // 请求失败之后要做的事（可以自定义）
                    // error(xhr.status);
                    reject(xhr.status);
                }
            }
        }
    })
}
